/*-------------------------------------------------------------------------
 *
 * mkdtemp.c
 *	  创建一个模式为 0700 的临时目录
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *	  src/port/mkdtemp.c
 *
 * 此代码源自 NetBSD，旨在为缺少它的平台提供实现。
 * （在符合许可证的实现中，OpenBSD 版本更能抵抗拒绝服务攻击。
 * 但是，它有加密依赖。）NetBSD 的版权条款如下。
 *-------------------------------------------------------------------------
 */

#include "c.h"

#define _DIAGASSERT(x) do {} while (0)


/*	$NetBSD: gettemp.c,v 1.17 2014/01/21 19:09:48 seanb Exp $	*/

/*
 * Copyright (c) 1987, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in the
 *	  documentation和/或其他提供的材料中。
 * 3. Neither the name of the University nor the names of its contributors
 *	  may be used to endorse或推广基于此软件衍生的产品，
 *	  除非获得特定的书面许可。
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, indirect, incidental, special, exemplary, 或后果性
 * 损害（包括但不限于获取替代商品
 * 或服务；使用、数据或利润的损失；或业务中断）所引起的损害
 * 无论在合同、严格责任或侵权（包括过失或其他）下，
 * 都不负任何责任
 * 即使被告知此类损害的可能性。
 */

#if HAVE_NBTOOL_CONFIG_H
#include "nbtool_config.h"
#endif

#if !HAVE_NBTOOL_CONFIG_H || !HAVE_MKSTEMP || !HAVE_MKDTEMP

#ifdef NOT_POSTGRESQL
#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)mktemp.c	8.1 (Berkeley) 6/4/93";
#else
__RCSID("$NetBSD: gettemp.c,v 1.17 2014/01/21 19:09:48 seanb Exp $");
#endif
#endif							/* LIBC_SCCS 和不是 lint */
#endif

#include <sys/types.h>
#include <sys/stat.h>

#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#ifdef NOT_POSTGRESQL
#if HAVE_NBTOOL_CONFIG_H
#define GETTEMP		__nbcompat_gettemp
#else
#include "reentrant.h"
#include "local.h"
#define GETTEMP		__gettemp
#endif
#endif

static int fc_GETTEMP(char *fc_path, int *fc_doopen, int fc_domkdir)
{
	char	   *fc_start,
			   *fc_trv;
	struct stat fc_sbuf;
	u_int		fc_pid;

	/*
	 * 为了确保多次调用生成唯一名称，即使文件未被创建。676 种不同的可能性，带有 7 个或更多 X，带有 6 个或更少的为 26。
	 */
	static char fc_xtra[2] = "aa";
	int			fc_xcnt = 0;

	_DIAGASSERT(path != NULL);
	/* doopen 可能为 NULL */

	fc_pid = getpid();

	/* 移动到路径的末尾并计算尾随的 X。 */
	for (fc_trv = fc_path; *fc_trv; ++fc_trv)
		if (*fc_trv == 'X')
			fc_xcnt++;
		else
			fc_xcnt = 0;

	/* 至少使用一个来自 xtra 的值。如果有超过 6 个 X，则使用 2。 */
	if (fc_xcnt > 0)
	{
		*--fc_trv = fc_xtra[0];
		fc_xcnt--;
	}
	if (fc_xcnt > 5)
	{
		*--fc_trv = fc_xtra[1];
		fc_xcnt--;
	}

	/* 将剩余的 X 设置为 pid 数字，左侧填充 0。 */
	for (; fc_xcnt > 0; fc_xcnt--)
	{
		*--fc_trv = (fc_pid % 10) + '0';
		fc_pid /= 10;
	}

	/* 为下一次调用更新 xtra。 */
	if (fc_xtra[0] != 'z')
		fc_xtra[0]++;
	else
	{
		fc_xtra[0] = 'a';
		if (fc_xtra[1] != 'z')
			fc_xtra[1]++;
		else
			fc_xtra[1] = 'a';
	}

	/*
	 * 检查目标目录；如果你有六个 X 且它不存在，这将运行 *非常* 长时间。
	 */
	for (fc_start = fc_trv + 1;; --fc_trv)
	{
		if (fc_trv <= fc_path)
			break;
		if (*fc_trv == '/')
		{
			int			fc_e;

			*fc_trv = '\0';
			fc_e = stat(fc_path, &fc_sbuf);
			*fc_trv = '/';
			if (fc_e == -1)
				return fc_doopen == NULL && !fc_domkdir;
			if (!S_ISDIR(fc_sbuf.st_mode))
			{
				errno = ENOTDIR;
				return fc_doopen == NULL && !fc_domkdir;
			}
			break;
		}
	}

	for (;;)
	{
		if (fc_doopen)
		{
			if ((*fc_doopen =
				 open(fc_path, O_CREAT | O_EXCL | O_RDWR, 0600)) >= 0)
				return 1;
			if (errno != EEXIST)
				return 0;
		}
		else if (fc_domkdir)
		{
			if (mkdir(fc_path, 0700) >= 0)
				return 1;
			if (errno != EEXIST)
				return 0;
		}
		else if (lstat(fc_path, &fc_sbuf))
			return errno == ENOENT ? 1 : 0;

		/* 用于向后兼容的巧妙算法 */
		for (fc_trv = fc_start;;)
		{
			if (!*fc_trv)
				return 0;
			if (*fc_trv == 'z')
				*fc_trv++ = 'a';
			else
			{
				if (isdigit((unsigned char) *fc_trv))
					*fc_trv = 'a';
				else
					++*fc_trv;
				break;
			}
		}
	}
	/* 不可达 */
}

#endif							/* !HAVE_NBTOOL_CONFIG_H || !HAVE_MKSTEMP ||
								 * !HAVE_MKDTEMP */


/*	$NetBSD: mkdtemp.c,v 1.11 2012/03/15 18:22:30 christos Exp $	*/

/*
 * Copyright (c) 1987, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in the
 *	  documentation和/或其他提供的材料中。
 * 3. Neither the name of the University nor the names of its contributors
 *	  may be used to endorse或推广基于此软件衍生的产品，
 *	  除非获得特定的书面许可。
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, indirect, incidental, special, exemplary, 或后果性
 * 损害（包括但不限于获取替代商品
 * 或服务；使用、数据或利润的损失；或业务中断）所引起的损害
 * 无论在合同、严格责任或侵权（包括过失或其他）下，
 * 都不负任何责任
 * 即使被告知此类损害的可能性。
 */

#if HAVE_NBTOOL_CONFIG_H
#include "nbtool_config.h"
#endif

#if !HAVE_NBTOOL_CONFIG_H || !HAVE_MKDTEMP

#ifdef NOT_POSTGRESQL

#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)mktemp.c	8.1 (Berkeley) 6/4/93";
#else
__RCSID("$NetBSD: mkdtemp.c,v 1.11 2012/03/15 18:22:30 christos Exp $");
#endif
#endif							/* LIBC_SCCS 和不是 lint */

#if HAVE_NBTOOL_CONFIG_H
#define GETTEMP		__nbcompat_gettemp
#else
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "reentrant.h"
#include "local.h"
#define GETTEMP		__gettemp
#endif

#endif

char * mkdtemp(char *fc_path)
{
	_DIAGASSERT(path != NULL);

	return fc_GETTEMP(fc_path, NULL, 1) ? fc_path : NULL;
}

#endif							/* !HAVE_NBTOOL_CONFIG_H || !HAVE_MKDTEMP */
